﻿using FI.BatchJob.Web.Constants;
using FI.BatchJob.Web.Data.Entities;
using FI.BatchJob.Web.Data.Entitities;
using FI.BatchJob.Web.Extensions;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Configuration;
using PubSub;
using Quartz;
using Serilog;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace FI.BatchJob.Web.QuartzNet
{
    /// <summary>
    /// create a custom listner to listen job running result.
    /// </summary>
    public class JobListener : IJobListener
    {
        public string Name => "JobListener";

        private Quartz.Impl.AdoJobStore.Common.IDbProvider _dbProvider;

        public static IConfiguration Configuration;

        static JobListener()
        {
            Configuration = BuildConfiguration();
        }
        public static IConfiguration BuildConfiguration()
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

            var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");


            if (!string.IsNullOrEmpty(env))
            {
                builder = builder.AddJsonFile($"appsettings.{env}.json", optional: true, reloadOnChange: true);
            }
            return Configuration = builder.Build();
        }

        public JobListener(Quartz.Impl.AdoJobStore.Common.IDbProvider dbProvider)
        {
            _dbProvider = dbProvider;


        }
        //job was Vetoed
        public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
        {
            await Console.Out.WriteLineAsync("job was vetoed.");
        }

        //job to be executed(before running)
        public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {

                ScheduleHistory history = context.JobDetail.JobDataMap.Get(Constant.SCHEDULE_HISTORY) as ScheduleHistory;

                if (history != null)
                {
                    var id = Guid.NewGuid().ToString();

                    var beginTime = DateTime.Now;
                    var jobStatus = EnumType.JobRunStatus.Running;
                    var createdTime = DateTime.Now;
                    var nextFireTime = context.NextFireTimeUtc.HasValue ? context.NextFireTimeUtc.Value.UtcDateTime.ToLocalTime() : (DateTime?)null;
                    var previousFireTime = context.PreviousFireTimeUtc.HasValue ? context.PreviousFireTimeUtc.Value.UtcDateTime.ToLocalTime() : (DateTime?)null;



                    using (var conn = _dbProvider.CreateConnection() as SqliteConnection)
                    {
                        conn.Open();

                        var insertSQL = _dbProvider.CreateCommand() as SqliteCommand;

                        insertSQL.Connection = conn;

                        insertSQL.CommandText = "INSERT INTO QRTZ_SCHEDULE_HISTORY (ID, JobName, JobGroup, JobDescription, " +
                            "TriggerType, Cron, RepeatTimes, Interval, JobStatus,PreviousFireTime,NextFireTime,BeginTime,Params," +
                            "AssemblyName,ClassName,CreatedTime) VALUES (@ID,@JobName,@JobGroup,@JobDescription,@TriggerType,@Cron,@RepeatTimes" +
                            ",@Interval,@JobStatus,@PreviousFireTime,@NextFireTime,@BeginTime,@Params,@AssemblyName,@ClassName,@CreatedTime)";

                        insertSQL.Parameters.AddWithValue("@ID", id);
                        insertSQL.Parameters.AddWithValue("@JobName", history.JobName);
                        insertSQL.Parameters.AddWithValue("@JobGroup", history.JobGroup);
                        insertSQL.Parameters.AddWithValue("@JobDescription", history.JobDescription ?? "");
                        insertSQL.Parameters.AddWithValue("@TriggerType", history.TriggerType);
                        insertSQL.Parameters.AddWithValue("@Cron", history.Cron);
                        insertSQL.Parameters.AddWithValue("@RepeatTimes", history.RepeatTimes);
                        insertSQL.Parameters.AddWithValue("@Interval", history.Interval);
                        insertSQL.Parameters.AddWithValue("@JobStatus", jobStatus);

                        if (nextFireTime == null)
                        {
                            insertSQL.Parameters.AddWithValue("@NextFireTime", DBNull.Value);
                        }
                        else
                        {
                            insertSQL.Parameters.AddWithValue("@NextFireTime", nextFireTime);
                        }

                        if (previousFireTime == null)
                        {
                            insertSQL.Parameters.AddWithValue("@PreviousFireTime", DBNull.Value);
                        }
                        else
                        {
                            insertSQL.Parameters.AddWithValue("@PreviousFireTime", previousFireTime);
                        }

                        insertSQL.Parameters.AddWithValue("@BeginTime", beginTime);
                        insertSQL.Parameters.AddWithValue("@Params", history.Params ?? "");
                        insertSQL.Parameters.AddWithValue("@AssemblyName", history.AssemblyName);
                        insertSQL.Parameters.AddWithValue("@ClassName", history.ClassName);
                        insertSQL.Parameters.AddWithValue("@CreatedTime", createdTime);

                        insertSQL.ExecuteNonQuery();

                    }



                    context.Put(Constant.SCHEDULE_HISTORY_ID, id);

                    await Hub.Default.PublishAsync<string>(id);

                }


            }
            catch (Exception ex)
            {
                Log.Error(ex.ToString());

            }


        }

        //after running
        public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                var historyid = context.Get(Constant.SCHEDULE_HISTORY_ID).ToString();

                var endTime = DateTime.Now;

                var notes = Convert.ToString(context.Result);

                EnumType.JobRunStatus jobStatus;

                var exception = string.Empty;

                if (jobException == null)
                {
                    jobStatus = EnumType.JobRunStatus.Completed;

                }
                else
                {
                    jobStatus = EnumType.JobRunStatus.CompletedWithException;

                    var innerExcep = jobException.InnerException;

                    if (innerExcep == null) innerExcep = jobException;

                    exception = innerExcep.ToString();

                    var title = "Task [" + context.JobDetail.Key.Group + "-" + context.JobDetail.Key.Name + "] have been done, but encoutered an error.";

                    var content = "<b style='color:red'>Exception Message：" + innerExcep.Message + "</b>" + "<br />" + "Detail:" + innerExcep.ToString();

                    EmailQueue.Instance.Enqueue(new ExceptionMail
                    {
                        Title = title,
                        Content = content
                    });

                }

                using (var conn = _dbProvider.CreateConnection() as SqliteConnection)

                {
                    conn.Open();
                    var updateSQL = _dbProvider.CreateCommand() as SqliteCommand;
                    updateSQL.Connection = conn;

                    updateSQL.CommandText = "UPDATE QRTZ_SCHEDULE_HISTORY SET EndTime=@EndTime,Notes=@Notes,JobStatus=@JobStatus,Exception=@Exception WHERE ID=@ID";
                    updateSQL.Parameters.AddWithValue("@EndTime", endTime);
                    updateSQL.Parameters.AddWithValue("@Notes", notes ?? "");
                    updateSQL.Parameters.AddWithValue("@JobStatus", jobStatus);
                    updateSQL.Parameters.AddWithValue("@Exception", exception ?? "");
                    updateSQL.Parameters.AddWithValue("@ID", historyid);

                    updateSQL.ExecuteNonQuery();

                }


                await Hub.Default.PublishAsync<string>(historyid);


            }
            catch (Exception ex)
            {
                Log.Error(ex.ToString());
            }

        }
    }

}
